#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "sockets.h"
#include "threads.h"
#include <string.h>

// the global server socket waiting for new clients
int serverSock;

int clientSocks[50]; // array of client sockets
int cSockCounter;
char clientNames[50][21];	// max 50 clients with names of 20 chars
struct sockaddr_in addrClients[50];
pthread_t clientThreads[50];

// we wont be using this now
// it's causing some trouble
// so we try it without.
// 	pthread_t broadcastingThread;

float clientPos[50][2];


void error(char *msg) {
	perror(msg);
	exit(1);
}

int startup(int port) {
	struct sockaddr_in addrServer;


	serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

	if(serverSock < 0) {
		error("Failed to create socket");
	}

	memset(&addrServer, 0, sizeof(addrServer));
	addrServer.sin_family = AF_INET;
	addrServer.sin_addr.s_addr = htonl(INADDR_ANY);
	addrServer.sin_port = htons(port);       // server port

	// Bind the server socket
	if(bind(serverSock, (struct sockaddr *) &addrServer, sizeof(addrServer)) < 0) {
		error("Failed to bind the server socket");
	}
	// Listen on the server socket
	if(listen(serverSock, 50) < 0) {	// 50 max-pending clients for now
		error("Failed to listen on server socket");
	}

	return 1;
}



int broadcastingThreadFunction(char *sendbuffer) {
	int ctr;
	//printf("sendbuffer = '%s'\n", sendbuffer);
	// some other thread wants us to broadcast
	// then do that.
	for(ctr=0; ctr<cSockCounter+1; ctr++) {
		if(clientSocks[ctr] == -1) continue;
		send(clientSocks[ctr], sendbuffer, strlen(sendbuffer), 0);
	}
}


// the server's mainloop waiting for clients
void mainloop() {
	unsigned int clientLen;
	cSockCounter = 0;

	while(1) {
		clientLen = sizeof(addrClients[cSockCounter]);

		// waiting for connection
		if((clientSocks[cSockCounter] = accept(serverSock, (struct sockaddr *) &addrClients[cSockCounter], &clientLen)) < 0) {
			error("Failed to accept client connection");
		}

		printf("Client %i connected: %s\n", cSockCounter, inet_ntoa(addrClients[cSockCounter].sin_addr));
		
		// create a new thread, specially for this client
		pthread_create(&clientThreads[cSockCounter], NULL, client_communication, (void *)cSockCounter);
		
		cSockCounter++;

	}



}

int proc_buffer(int socknum, char *sendbuffer, char *buffer) {
	// see http://code.google.com/p/dotgame-of-community/wiki/DOCProtocol
	char arg1[200], cx[50], cy[50];
	char cmd[200];
	char brdcmd[60], buffer2[200], ctmp[200];
	int j;
	float x,y;
	
	strcpy(buffer2, buffer);	

	get_part(buffer2, cmd, 0);
	
	
	if(strncmp(cmd, "HI", 2)==0) {
		get_part(buffer2, arg1, 1);
		strcpy(clientNames[socknum], arg1);
		
		// now send a NEW to all clients
		// we do this by a Broadcast:
		printf("The name  of client %i is: '%s'\n", socknum, clientNames[socknum]);
		sprintf(brdcmd, "NEW %i %s \n", socknum, clientNames[socknum]);
		//pthread_create(&broadcastingThread, NULL, broadcastingThreadFunction, (void *)brdcmd);
		broadcastingThreadFunction(brdcmd);
		
		return 1;
	}
	
	if(strncmp(cmd, "POS", 3)==0) {
		get_part(buffer2, cx, 1);
		get_part(buffer2, cy, 2);
		x = atof(cx);
		y = atof(cy);
		
		clientPos[socknum][X] = x;
		clientPos[socknum][Y] = y;
		
		// broadcast 'POS [id] x,y'
		sprintf(brdcmd, "POS %i %f,%f \n", socknum, x, y);
		//pthread_create(&broadcastingThread, NULL, broadcastingThreadFunction, (void *)brdcmd);
		broadcastingThreadFunction(brdcmd);
	}
	
	if(strncmp(cmd, "NAME", 4)==0) {
		get_part(buffer2, arg1, 1);	// new name in arg1
		strcpy(clientNames[socknum], arg1);	// change the name

		// broadcast 'NAME [id] [newname]'
		sprintf(brdcmd, "NAME %i %s \n", socknum, clientNames[socknum]);
		//pthread_create(&broadcastingThread, NULL, broadcastingThreadFunction, (void *)brdcmd);
		broadcastingThreadFunction(brdcmd);
	}
	
	if(strncmp(cmd, "BYE", 3)==0) {
		sprintf(brdcmd, "BYE %i \n", socknum);
		//pthread_create(&broadcastingThread, NULL, broadcastingThreadFunction, (void *)brdcmd);
		broadcastingThreadFunction(brdcmd);
		
		// wait till the thread finishes
		//pthread_join(broadcastingThread, NULL);
		
		close(clientSocks[socknum]);
		clientSocks[socknum] = -1;
		printf("Client %i disconnected.\n", socknum);
		// broadcast 'BYE [id]'
		return 4; // to say the client disconnected
	}
	
	if(strncmp(cmd, "LST", 3)==0) {
		for(j=0; j<cSockCounter; j++) {
			if(clientSocks[j] == -1) continue;
			sprintf(sendbuffer, "%sCLI %i %s %f,%f|", sendbuffer, j, clientNames[j], clientPos[j][X], clientPos[j][Y]);
		}
		sprintf(sendbuffer, "%s\n", sendbuffer);
	}
	
	return 0;
	
}

void *client_communication(void *ptr) {
	int socknum, sock;
	socknum = (int)ptr;

	sock = clientSocks[socknum];

	char buffer[200], sendbuffer[200];
	int received = -1;
	if((received = recv(sock, buffer, 200, 0)) < 0) {
		error("Failed to receive initial bytes from client");
	}
	proc_buffer(socknum, sendbuffer, buffer);
	
	// Send data and check for more
	while(received > 0) {
		// send some shit back
		send(sock, sendbuffer, strlen(sendbuffer), 0);
		memset(sendbuffer, '\0', sizeof(sendbuffer));
		
		memset(buffer, '\0', sizeof(buffer));
		// check for more
		if((received = recv(sock, buffer, 200, 0)) < 0) {
			error("Failed to receive more data from client");
		}
		if(proc_buffer(socknum, sendbuffer, buffer) == 4) {
			return; // client disconnected
		}
	}
	close(sock);
}



